package com.wyj.config;

import com.alibaba.dubbo.rpc.support.RpcUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * TODO: 
 * 
 * @author wuyijie
 * @date 2022/1/15 4:25 PM
 */
@Activate(group = {CommonConstants.PROVIDER,CommonConstants.CONSUMER})
@Slf4j
public class TPMonitorFilter implements Filter {
    private static final Map<LocalDateTime, Statics> COUNT_MAP = new ConcurrentHashMap<>();
    static final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8);

    public TPMonitorFilter() {
        log.info("***************");
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            log.info("------------------");
            final var now = LocalDateTime.now();
            final var entries = COUNT_MAP.entrySet();
            final var iterator = entries.iterator();
            final var objects = new ArrayList<Statics>();

            try {
                while (iterator.hasNext()) {
                    final var next = iterator.next();
                    //获取一分钟内的请求统计数据
                    if (Duration.between(next.getKey(), now).toMinutes() < 1) {
                        objects.add(next.getValue());
                    }else {
                        iterator.remove();
                    }
                }
                objects.sort(new Comparator<Statics>() {
                    @Override
                    public int compare(Statics o1, Statics o2) {
                        if (o1.getTotalTime() < o2.getTotalTime()) {
                            return -1;
                        } else if (o1.getTotalTime().equals(o2.getTotalTime())) {
                            return 0;
                        } else {
                            return 1;
                        }
                    }
                });
            }catch (Exception e){
                e.printStackTrace();
            }
           if (!CollectionUtils.isEmpty(objects)){
               final var tp90 = objects.get((int) (objects.size() * 0.9));
               final var tp99 = objects.get((int) (objects.size() * 0.99));
               log.info("【当前时间:{}，总请求数={},tp90={},tp99={}】", now, objects.size(), tp90, tp99);
           }
        }, 5, 5, TimeUnit.SECONDS);
    }


    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //判断是否是异步调用
        final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
        if (!isAsync) {
            String method = invocation.getMethodName();
            String name = invoker.getInterface().getName();
            Object[] args = invocation.getArguments();
            String ageString = JSON.toJSONString(args);

            final var start = LocalDateTime.now();
            final var result = invoker.invoke(invocation);
            final var end = LocalDateTime.now();
            final var between = Duration.between(start, end);
            final var l = between.toMillis();
            final var map = new Statics(name + method, l);
            COUNT_MAP.put(start, map);
            return result;
        }
        return invoker.invoke(invocation);

    }


    static class Statics {
        private String methodName;
        private Long totalTime;

        public Statics(String methodName, Long totalTime) {
            this.methodName = methodName;
            this.totalTime = totalTime;
        }

        public String getMethodName() {
            return methodName;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public Long getTotalTime() {
            return totalTime;
        }

        public void setTotalTime(Long totalTime) {
            this.totalTime = totalTime;
        }

        @Override
        public String toString() {
            return "Statics{" +
                    "methodName='" + methodName + '\'' +
                    ", totalTime=" + totalTime +
                    '}';
        }
    }
}
